package service

import (
	"context"
	"errors"
	"gitee.com/DevilYyx/common"
	"gitee.com/DevilYyx/pod/domain/model"
	"gitee.com/DevilYyx/pod/domain/repository"
	"gitee.com/DevilYyx/pod/proto/pod"
	"gorm.io/gorm"
	v1 "k8s.io/api/apps/v1"
	v13 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"strconv"
)

const (
	Deployment   = "Deployment"
	V1Version    = "v1"
	AppNameLabel = "app-name"
)

type IPodService interface {
	AddPod(pod *model.Pod) (int64, error)
	DeletePod(int64) error
	UpdatePod(*model.Pod) error
	FindPodByID(int64) (*model.Pod, error)
	FindAllPod() ([]model.Pod, error)
	CreateToK8s(*pod.PodInfo) (podId int64, err error)
	DeleteFromK8s(*model.Pod) error
	UpdateToK8s(*pod.PodInfo) error
	GetDB() *gorm.DB
}

type PodService struct {
	repository repository.IPodRepository
	client     *kubernetes.Clientset
	deployment *v1.Deployment
}

func NewPodService(repository repository.IPodRepository, client *kubernetes.Clientset) *PodService {
	return &PodService{
		repository: repository,
		client:     client,
		deployment: &v1.Deployment{},
	}
}
func (s *PodService) AddPod(pod *model.Pod) (int64, error) {
	//TODO implement me
	return s.repository.CreatePod(pod)
}

func (s *PodService) DeletePod(id int64) error {
	return s.repository.DeletePodById(id)
}

func (s *PodService) UpdatePod(pod *model.Pod) error {
	return s.repository.UpdatePod(pod)
}

func (s *PodService) FindPodByID(id int64) (*model.Pod, error) {
	return s.repository.FindPodById(id)
}

func (s *PodService) FindAllPod() ([]model.Pod, error) {
	return s.repository.FindAll()
}

func (s *PodService) CreateToK8s(pod *pod.PodInfo) (podId int64, err error) {
	s.SetDeployment(pod)
	// 判定是否存在
	_, err = s.client.AppsV1().Deployments(pod.PodNamespace).Get(context.TODO(), pod.PodName, v12.GetOptions{})
	if err == nil {
		return 0, errors.New("Pod " + pod.PodName + " 已经存在")
	}
	// 开启事务
	tx := s.GetDB()
	tx.Begin()
	// 事务回滚
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
		}
	}()
	// 填充数据
	podModel := &model.Pod{}
	err = common.SwapTo(pod, podModel)
	if err != nil {
		return 0, err
	}
	// 创建数据到数据库中
	podId, err = s.repository.CreatePod(podModel)
	if err != nil {
		tx.Rollback()
		return 0, err
	}
	_, err = s.client.AppsV1().Deployments(pod.PodNamespace).Create(context.TODO(), s.deployment, v12.CreateOptions{})
	if err != nil {
		tx.Rollback()
		common.Error(err)
		return 0, err
	}
	common.Info(pod.PodName + " 创建成功")
	return podId, err
}

func (s *PodService) DeleteFromK8s(pod *model.Pod) (err error) {
	if err = s.client.AppsV1().Deployments(pod.PodNamespace).Delete(context.TODO(), pod.PodName, v12.DeleteOptions{}); err != nil {
		common.Error(err)
		return err
	} else {
		if err := s.DeletePod(pod.ID); err != nil {
			common.Error(err)
			return err
		}
		common.Info("删除Pod ID：" + strconv.FormatInt(pod.ID, 10) + " 成功！")
	}
	return
}

func (s *PodService) UpdateToK8s(pod *pod.PodInfo) (err error) {
	s.SetDeployment(pod)
	if _, err = s.client.AppsV1().Deployments(pod.PodNamespace).Get(context.TODO(), pod.PodName, v12.GetOptions{}); err != nil {
		common.Error(err)
		return errors.New("Pod " + pod.PodName + " 不存在请先创建")
	} else {
		//如果存在
		if _, err = s.client.AppsV1().Deployments(pod.PodNamespace).Update(context.TODO(), s.deployment, v12.UpdateOptions{}); err != nil {
			common.Error(err)
			return err
		}
		common.Info(pod.PodName + " 更新成功")
		return
	}
}

func (s *PodService) GetDB() *gorm.DB {
	return s.repository.GetDB()
}
func (s *PodService) SetDeployment(pod *pod.PodInfo) {
	deployment := &v1.Deployment{}
	deployment.TypeMeta = v12.TypeMeta{
		Kind:       Deployment,
		APIVersion: V1Version,
	}
	deployment.ObjectMeta = v12.ObjectMeta{
		Name:      pod.PodName,
		Namespace: pod.PodNamespace,
		Labels: map[string]string{
			AppNameLabel: pod.PodName,
		},
		Annotations: map[string]string{},
	}
	deployment.Spec = v1.DeploymentSpec{
		Replicas: &pod.PodReplicas,
		Selector: &v12.LabelSelector{
			MatchLabels: map[string]string{
				AppNameLabel: pod.PodName,
			},
			MatchExpressions: nil,
		},
		Template: v13.PodTemplateSpec{
			ObjectMeta: v12.ObjectMeta{
				Labels: map[string]string{
					AppNameLabel: pod.PodName,
				},
			},
			Spec: v13.PodSpec{
				RestartPolicy: v13.RestartPolicyAlways,
				Containers: []v13.Container{
					{
						Name:            pod.PodName,
						Image:           pod.PodImage,
						Ports:           s.getContainerPort(pod),
						Env:             s.getEnv(pod),
						Resources:       s.getResources(pod),
						ImagePullPolicy: s.getImagePullPolicy(pod),
					},
				},
			},
		},
		Strategy: v1.DeploymentStrategy{
			Type: v1.RollingUpdateDeploymentStrategyType,
			RollingUpdate: &v1.RollingUpdateDeployment{
				MaxUnavailable: &intstr.IntOrString{
					Type:   intstr.Int,
					IntVal: 25,
					StrVal: "",
				},
				MaxSurge: &intstr.IntOrString{
					Type:   intstr.Int,
					IntVal: 25,
					StrVal: "",
				},
			},
		},
		MinReadySeconds:         0,
		RevisionHistoryLimit:    nil,
		Paused:                  false,
		ProgressDeadlineSeconds: nil,
	}
	s.deployment = deployment
}

func (s *PodService) getContainerPort(pod *pod.PodInfo) (containerPorts []v13.ContainerPort) {
	for _, port := range pod.PodPort {
		containerPorts = append(containerPorts, v13.ContainerPort{
			Name:          "port-" + strconv.FormatInt(int64(port.ContainerPort), 10),
			ContainerPort: port.ContainerPort,
			Protocol:      s.getProtocol(port.Protocol),
		})
	}
	return containerPorts
}

func (s *PodService) getProtocol(protocol string) v13.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}
}

func (s *PodService) getEnv(pod *pod.PodInfo) (envs []v13.EnvVar) {
	for _, env := range pod.PodEnv {
		envs = append(envs, v13.EnvVar{
			Name:      env.EnvKey,
			Value:     env.EnvValue,
			ValueFrom: nil,
		})
	}
	return envs
}

func (s *PodService) getResources(pod *pod.PodInfo) (source v13.ResourceRequirements) {
	//最大能够使用多少资源
	source.Limits = v13.ResourceList{
		v13.ResourceCPU:    resource.MustParse(strconv.FormatFloat(float64(pod.PodCpuMin), 'f', 6, 64)),
		v13.ResourceMemory: resource.MustParse(strconv.FormatFloat(float64(pod.PodMemoryMin), 'f', 6, 64)),
	}
	//满足最少使用的资源量
	//@TODO 自己实现动态设置
	source.Requests = v13.ResourceList{
		v13.ResourceCPU:    resource.MustParse(strconv.FormatFloat(float64(pod.PodCpuMax), 'f', 6, 64)),
		v13.ResourceMemory: resource.MustParse(strconv.FormatFloat(float64(pod.PodMemoryMax), 'f', 6, 64)),
	}
	return source
}

func (s *PodService) getImagePullPolicy(pod *pod.PodInfo) (policy v13.PullPolicy) {
	switch pod.PodPullPolicy {
	case string(v13.PullAlways):
		return v13.PullAlways
	case string(v13.PullIfNotPresent):
		return v13.PullIfNotPresent
	case string(v13.PullNever):
		return v13.PullNever
	default:
		return v13.PullAlways
	}
}
